﻿using System;
using System.Collections.Generic;
using System.Text;
using Ivi.Driver.Interop;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using Ivi.ConfigServer.Interop;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.ComponentModel;
using AIRO.Devices.PositioningDevice;
using System.Diagnostics;


namespace AIRO.Devices.Solenoid
{
    #region enums & structs
    [Guid("6364F722-4392-11DD-AB5A-482256D89593")]
    public enum ControlStates : byte
    {
        LocalAndLocked = 0,
        RemoteAndLocked = 1,
        LocalAndUnlocked = 2,
        RemoteAndUnlocked = 3
    }

    [Guid("55EE0EA8-4392-11DD-A5E2-332156D89593")]
    public enum CommunicationProtocols : byte
    {
        Normal = 0,
        SendsLFAfterEachCR = 2,
        ExtendedResolution = 4,
        ExtendedResolutionSendsLFAfterEachCR = 6
    }

    [Guid("4B1B27B8-4392-11DD-A90B-AD2056D89593")]
    public enum SystemStatusesM : byte
    {
        Normal = 0,
        Quenched = 1,
        OverHeated = 2,
        WarmingUp = 4,
        Fault = 8
    }

    [Guid("3A4BFFFC-4392-11DD-B56A-921F56D89593")]
    public enum SystemStatusesN : byte
    {
        Normal = 0,
        OnPositiveVoltageLimit = 1,
        OnNegativeVoltageLimit = 2,
        OutsideNegativeCurrentLimit = 4,
        OutsidePositiveCurrentLimit = 8
    }

    [Guid("34A97CDC-4392-11DD-B902-721F56D89593")]
    public enum ActivityStatuses : byte
    {
        Hold = 0,
        ToSetPoint = 1,
        ToZero = 2,
        Clamped = 4
    }

    [Guid("22EA1FA6-4392-11DD-9378-421E56D89593")]
    public enum SwitchHeaterStatuses : byte
    {
        OffMagnetAtZero = 0,
        On = 1,
        OffMagnetAtField = 2,
        HeaterFault = 5,
        NoSwitchFitted = 8
    }

    [Guid("F067D204-43A4-11DD-889C-057355D89593")]
    public enum ModesN : byte
    {
        AtRest = 0,
        Sweeping = 1,
        SweepLimiting = 2,
        SweepingAndSweepLimiting = 3,
        PolarityFault = 4,
        SweepingAndPolarityFault = 5
    }



    [Guid("7FD67B92-4392-11DD-97A5-002456D89593")]
    public struct IPS120_10Status
    {
        private SystemStatusesM _systemStatusM;
        private SystemStatusesN _systemStatusN;
        private ActivityStatuses _activityStatus;
        private ControlStates _controlState;
        private SwitchHeaterStatuses _switchHeaterStatus;
        private byte _modeM;
        private ModesN _modeN;

        public SystemStatusesM SystemStatusM
        {
            set { _systemStatusM = value; }
            get { return _systemStatusM; }
        }
        public SystemStatusesN SystemStatusN
        {
            set { _systemStatusN = value; }
            get { return _systemStatusN; }
        }
        public ActivityStatuses ActivityStatus
        {
            set { _activityStatus = value; }
            get { return _activityStatus; }
        }
        public ControlStates ControlState
        {
            set { _controlState = value; }
            get { return _controlState; }
        }
        public SwitchHeaterStatuses SwitchHeaterStatus
        {
            set { _switchHeaterStatus = value; }
            get { return _switchHeaterStatus; }
        }
        public byte ModeM
        {
            set { _modeM = value; }
            get { return _modeM; }
        }
        public ModesN ModeN
        {
            set { _modeN = value; }
            get { return _modeN; }
        }
    }

    #endregion

    [Guid("269D4A9C-3D61-11DD-8D33-29F055D89593")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("MyDevices.Solenoid.IPS120_10")]
    public class IPS120_10 : BasicIviDriverVisa, Ivi.Driver.Interop.IIviDriver, IIviDriverIdentity, IIviComponentIdentity, IIviDriverOperation, IIviDriverUtility, ISolenoid, IPositioningDevice
    {
        private const double maxCurrentLimit = 150;
        private double currentLimit;
        private double coilConst;
        private SolenoidState currentState;
        private double _accuracy;
        bool fieldSettled = false;
        private System.ComponentModel.BackgroundWorker backgroundWorker;

        public IPS120_10()
            : base()
        {            
            backgroundWorker = new BackgroundWorker();
            InitializeBackgoundWorker();
            _description = "IVI Custom Driver for IPS120-10";
            _identifier = "IPS120-10";
            _instrumentManufacturer = "Oxford Instruments";
            _supportedInstrumentModels = "IPS120-10";
            _vendor = "Abteilung v. Klitzing, FKF-MPI";
        }

        #region Properties



        public double DemandCurrent
        {
            get
            {
                return ReadDouble("R0");
            }
        }

        public double SupplyVoltage
        {
            get
            {
                return ReadDouble("R1");
            }
        }

        public double MeasuredMagnetCurrent
        {
            get
            {
                return ReadDouble("R2");
            }
        }

        public double ProgrammedCurrent
        {
            get
            {
                return ReadDouble("R5");
            }
            set
            {
                if (_initialized)
                {
                    if (Math.Abs(value) <= currentLimit)
                    {
                        string cmd = String.Format(numericFormatProvider, "I{0:0.000}", value);
                        string res = Query(cmd);
                        ParseResponseAndReportIfError(res);
                    }
                    else
                    {
                        SolenoidState state = SolenoidState.Error;
                        OnStateChangedEvent(new Solenoid.StateChangedEventArgs(state));
                        OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Error));
                        currentState = state;
                    }
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    throw new Exception("Driver is not initialized");
                }
            }
        }

        public double RampRateCurrent
        {
            get
            {
                return ReadDouble("R6");
            }
            set
            {
                if (_initialized)
                {
                    string cmd = String.Format(numericFormatProvider, "S{0:0.00}", value);
                    string res = Query(cmd);
                    ParseResponseAndReportIfError(res);
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    throw new Exception("Driver is not initialized");
                }
            }
        }

        public double MagnetField
        {
            get
            {
                return ReadDouble("R7");
            }
        }

        public double ProgrammedField
        {
            get
            {
                return ReadDouble("R8");
            }
            set
            {
                if (_initialized)
                {
                    if (Math.Abs(value) <= coilConst * currentLimit)
                    {
                        string cmd = String.Format(numericFormatProvider, "J{0:0.0000}", value);
                        string res = Query(cmd);
                        ParseResponseAndReportIfError(res);
                    }
                    else
                    {
                        SolenoidState state = SolenoidState.Error;
                        OnStateChangedEvent(new Solenoid.StateChangedEventArgs(state));
                        OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Error));
                        currentState = state;
                    }
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    throw new Exception("Driver is not initialized");
                }

            }
        }

        public double RampRateField
        {
            get
            {
                return ReadDouble("R9");
            }
            set
            {
                if (_initialized)
                {
                    string cmd = String.Format(numericFormatProvider, "T{0:0.000}", value);
                    string res = Query(cmd);
                    ParseResponseAndReportIfError(res);
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    throw new Exception("Driver is not initialized");
                }
            }
        }

        public double VoltageLimit
        {
            get
            {
                return ReadDouble("R15");
            }
        }

        public double PersistentMagnetCurrent
        {
            get
            {
                return ReadDouble("R16");
            }
        }

        public double TripCurrent
        {
            get
            {
                return ReadDouble("R17");
            }
        }

        public double PersistentMagnetField
        {
            get
            {
                return ReadDouble("R18");
            }
        }

        public double TripField
        {
            get
            {
                return ReadDouble("R19");
            }
        }

        public double SwitchHeaterCurrent
        {
            get
            {
                return ReadDouble("R20");
            }
        }

        public double SafeCurrentLimitPos
        {
            get
            {
                return ReadDouble("R21");
            }
        }

        public double SafeCurrentLimitNeg
        {
            get
            {
                return ReadDouble("R22");
            }
        }

        public double LeadResistance
        {
            get
            {
                return ReadDouble("R23");
            }
        }

        public double MagnetInductance
        {
            get
            {
                return ReadDouble("R24");
            }
        }

        public double Accuracy
        {
            get
            {
                if (_initialized)
                {
                    return _accuracy;
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    throw new Exception("Driver is not initialized");
                }
            }
            set
            {
                if (_initialized)
                {
                    if (value > 0)
                    {
                        _accuracy = value;
                    }
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    throw new Exception("Driver is not initialized");
                }
            }
        }

        SolenoidState ISolenoid.State
        {
            get
            {
                if (_initialized)
                {
                    return getSolenoidState();
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    throw new Exception("Driver is not initialized");                    
                }
            }
        }

        private bool getStatus(out IPS120_10Status status)
        {
            string cmd = "X";
            string statusString = Query(cmd);
            status = new IPS120_10Status();
            const string statusStringPattern = @"X(?<SystemStatusM>\d)(?<SystemStatusN>\d)A(?<ActivityStatus>\d)C(?<ControlState>\d)H(?<SwitchHeaterStatus>\d)M(?<ModeM>\d)(?<ModeN>\d)P(?<PolarityM>\d)(?<PolarityN>\d)";
            Regex statusStringRegex = new Regex(statusStringPattern, RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Match m = statusStringRegex.Match(statusString);
            if (m.Success)
            {
                status.SystemStatusM = (SystemStatusesM)System.Convert.ToByte(m.Groups["SystemStatusM"].Value);
                status.SystemStatusN = (SystemStatusesN)System.Convert.ToByte(m.Groups["SystemStatusN"].Value);
                status.ActivityStatus = (ActivityStatuses)System.Convert.ToByte(m.Groups["ActivityStatus"].Value);
                status.ControlState = (ControlStates)System.Convert.ToByte(m.Groups["ControlState"].Value);
                status.SwitchHeaterStatus = (SwitchHeaterStatuses)System.Convert.ToByte(m.Groups["SwitchHeaterStatus"].Value);
                status.ModeM = System.Convert.ToByte(m.Groups["ModeM"].Value);
                status.ModeN = (ModesN)System.Convert.ToByte(m.Groups["ModeN"].Value);
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region Public Methods

        public void SetControl(ControlStates state)
        {
            if (_initialized)
            {
                string cmd = "C" + System.Convert.ToInt32(state);
                string res = Query(cmd);
            }

            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void SetCommunicationProtocol(CommunicationProtocols protocol)
        {
            if (_initialized)
            {
                string cmd = "Q" + System.Convert.ToInt32(protocol);
                SendCommand(cmd);
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void Pause()
        {
            if (_initialized)
            {
                string cmd = "A0";
                string res = Query(cmd);
                ParseResponseAndReportIfError(res);
                CheckAndReportState();
                //BeginQuery(cmd,new AsyncCallback(OnOperationComplete));
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void BeginRamp()
        {
            if (_initialized)
            {
                fieldSettled = false;
                if (currentState != SolenoidState.Running)
                {
                    currentState = SolenoidState.Running;
                    OnStateChangedEvent(new Solenoid.StateChangedEventArgs(SolenoidState.Running));
                    OnStateChangedEvent(new PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Running));
                }
                string cmd = "A1";
                backgroundWorker.RunWorkerAsync(cmd);
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        private string QueryAndWaitForSettling(string cmd, BackgroundWorker worker, DoWorkEventArgs e)
        {
            //string cmd = (string)objCmd;

            string res = Query(cmd);

            if (res[0] == '?')
                return "0";
            else
            {
                double targetField;
                double field;
                SolenoidState state;
               
                do
                {
                    ReadDouble("R7", out field);
                    state = this.getSolenoidState();
                    ReadDouble("R8", out targetField);
                    //worker.ReportProgress(0, field);
                    if (worker.CancellationPending)
                    {
                        e.Cancel = true;
                        return "1";
                    }
                    worker.ReportProgress(0, field);

                } while ((Math.Abs(field - targetField) > _accuracy) | (state == SolenoidState.Running));
                /*
                do
                {
                    if (worker.CancellationPending)
                    {
                        e.Cancel = true;
                        return "1";
                    }
                } while (this.getSolenoidState() != SolenoidState.Idle);
                 * */
                fieldSettled = true;
                return "1";
            }


        }

        public string QueryAndWaitForSettling(string cmd)
        {
            string res = Query(cmd);

            if (res[0] == '?')
                return "0";
            else
            {
                double targetField;
                double field;
                SolenoidState state;
                
                do
                {
                    ReadDouble("R7", out field);
                    state = this.getSolenoidState();
                    ReadDouble("R8", out targetField);                    
                } while ((Math.Abs(field - targetField) > _accuracy) | (state == SolenoidState.Running));
                 /*
                do
                {                  
                                        
                } while (this.getSolenoidState() != SolenoidState.Idle);
                  * */
                fieldSettled = true;
                return "1";
            }
        }

        public void SetSwitchHeater(SwitchHeaterStatuses status)
        {
            if (_initialized)
            {
                byte st = (byte)status;
                if (st <= 2)
                {
                    string cmd = 'H' + st.ToString();
                    string res = Query(cmd);
                }
                else
                    throw new ArgumentException();
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void BeginGotoField(double field)
        {
            if (_initialized)
            {
                ProgrammedField = field;
                BeginRamp();
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void GoToZero()
        {
            if (_initialized)
            {
                string cmd = "A2";                
                if (currentState != SolenoidState.Running)
                {
                    currentState = SolenoidState.Running;
                    OnStateChangedEvent(new Solenoid.StateChangedEventArgs(SolenoidState.Running));
                    OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Running));
                }
                string res = Query(cmd);
                ParseResponseAndReportIfError(res);
                CheckAndReportState();
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void Clamp()
        {
            if (_initialized)
            {
                string cmd = "A4";
                string res = Query(cmd);
                ParseResponseAndReportIfError(res);
                CheckAndReportState();
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        public void Cancel()
        {
            if (_initialized)
            {
                if (backgroundWorker.IsBusy)
                    backgroundWorker.CancelAsync();
                string cmd = "A0";
                string res = Query(cmd);
                ParseResponseAndReportIfError(res);
                CheckAndReportState();
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }

        }

        public override string InstrumentModel
        {
            get
            {
                if (_initialized)
                {
                    if (Simulate)
                        return "Not available while simulating";
                    else
                    {
                        string cmd = "V";
                        string response = Query(cmd);
                        return ParseResponseAndReportIfError(response);
                    }
                }
                else
                {
                    iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                    return "";
                }
            }

        }

        #endregion



        #region Private Methods

        private SolenoidState getSolenoidState()
        {
            IPS120_10Status status;
            if (!getStatus(out status))
            {
                return SolenoidState.Error;
            }
            else
            {

                if ((status.SystemStatusM == SystemStatusesM.Fault) | (status.SystemStatusM == SystemStatusesM.OverHeated))
                    return SolenoidState.Error;
                else if (status.SystemStatusM == SystemStatusesM.Quenched)
                    return SolenoidState.QuenchDetected;
                else
                {
                    if (status.SystemStatusN != SystemStatusesN.Normal)
                    {
                        return SolenoidState.Error;
                    }
                    else
                    {
                        switch (status.ModeN)
                        {
                            case ModesN.AtRest:
                                return SolenoidState.Idle;
                            case ModesN.Sweeping:
                                return SolenoidState.Running;
                            default:
                                return SolenoidState.Error;

                        }

                    }
                }
            }
        }

        private string ParseResponseAndReportIfError(string response)
        {
            if (response[0] == '?')
            {
                SolenoidState state = SolenoidState.Error;
                OnStateChangedEvent(new Solenoid.StateChangedEventArgs(state));
                OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Error));
                currentState = state;
                return "?";
            }
            else if (Char.IsLetter(response[0]))
            {
                return (response.Substring(1));
            }
            else
                return response;
        }

        private double ReadDouble(string cmd)
        {
            double res;
            if (_initialized)
            {
                if (!ReadDouble(cmd, out res))
                {
                    SolenoidState state = SolenoidState.Error;
                    OnStateChangedEvent(new Solenoid.StateChangedEventArgs(state));
                    OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Error));
                    currentState = state;
                }
                return res;
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }

        private bool ReadDouble(string cmd, out double result)
        {
            //string cmd = "R0";
            string response = Query(cmd);
            if (response[0] == '?')
            {
                result = 0;
                return false;
            }
            else if (Char.IsLetter(response[0]))
            {
                result = System.Convert.ToDouble(response.Substring(1), numericFormatProvider);
                return true;
            }
            else
            {
                result = System.Convert.ToDouble(response, numericFormatProvider);
                return true;
            }

        }

        private void CheckAndReportState()
        {
            SolenoidState state = getSolenoidState();

            if (currentState != state)
            {
                OnStateChangedEvent(new Solenoid.StateChangedEventArgs(state));
                OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(ConvertState(state)));                
                currentState = state;
            }
            if (fieldSettled)
            {
                double mf = MagnetField;
                OnFieldSetEvent(new FieldSetEventArgs(mf));
                OnFinishEvent(new FinishEventArgs(mf));
                fieldSettled = false;
            }
        }

        private PositioningDeviceState ConvertState(SolenoidState state)
        {
            switch (state)
            {
                case SolenoidState.Error:
                case SolenoidState.QuenchDetected:
                    return PositioningDeviceState.Error;
                case SolenoidState.HeatingPersistentSwitch:
                case SolenoidState.Idle:
                case SolenoidState.Paused:
                    return PositioningDeviceState.Idle;
                case SolenoidState.Running:
                    return PositioningDeviceState.Running;
                default:
                    return PositioningDeviceState.Error;
            }
        }

        /// <summary>
        /// Set up the BackgroundWorker object by attaching event handlers.
        /// </summary>
        private void InitializeBackgoundWorker()
        {
            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
            backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
            backgroundWorker.WorkerReportsProgress = true;
            backgroundWorker.WorkerSupportsCancellation = true;
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error == null)
                if (!e.Cancelled)
                {
                    if ((string)e.Result == "1")
                    {
                        CheckAndReportState();
                        return;
                    }
                }
                else
                {
                    CheckAndReportState();
                    return;
                }
            SolenoidState state = SolenoidState.Error;
            OnStateChangedEvent(new Solenoid.StateChangedEventArgs(state));
            OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Error));
            currentState = state;
            return;
        }

        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            OnProgressEvent(new Solenoid.ProgressEventArgs((double)e.UserState));
            OnProgressEvent(new AIRO.Devices.PositioningDevice.ProgressEventArgs((double)e.UserState));
            CheckAndReportState();
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            e.Result = QueryAndWaitForSettling((string)e.Argument, worker, e);            
        }

        #endregion

        private EventHandler<AIRO.Devices.Solenoid.StateChangedEventArgs> solenoidStateChangedEvent;
        private EventHandler<AIRO.Devices.Solenoid.FieldSetEventArgs> solenoidFieldSetEvent;
        private EventHandler<AIRO.Devices.Solenoid.ProgressEventArgs> solenoidProgressEvent;

        event EventHandler<Solenoid.StateChangedEventArgs> ISolenoid.StateChangedEvent
        {
            add
            {
                solenoidStateChangedEvent += value;
            }
            remove
            {
                solenoidStateChangedEvent -= value;
            }
        }
        event EventHandler<Solenoid.FieldSetEventArgs> ISolenoid.FieldSetEvent
        {
            add
            {
                solenoidFieldSetEvent += value;
            }
            remove
            {
                solenoidFieldSetEvent -= value;
            }
        }
        event EventHandler<Solenoid.ProgressEventArgs> ISolenoid.ProgressEvent
        {
            add
            {
                solenoidProgressEvent += value;
            }
            remove
            {
                solenoidProgressEvent -= value;
            }
        }

        protected virtual void OnStateChangedEvent(Solenoid.StateChangedEventArgs e)
        {
            EventHandler<Solenoid.StateChangedEventArgs> handler = solenoidStateChangedEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnFieldSetEvent(Solenoid.FieldSetEventArgs e)
        {
            EventHandler<Solenoid.FieldSetEventArgs> handler = solenoidFieldSetEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnProgressEvent(Solenoid.ProgressEventArgs e)
        {
            EventHandler<Solenoid.ProgressEventArgs> handler = solenoidProgressEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #region IIviDriver Members


        public override void Close()
        {
            if (_initialized)
            {
                string cmd = "A0";
                string res = Query(cmd);
                ParseResponseAndReportIfError(res);
                CheckAndReportState();
                instrument.IO.UnlockRsrc();
                instrument.IO.Close();
                
                //mbSession.Dispose();
                _initialized = false;

            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
                throw new Exception("Driver is not initialized");
            }
        }


        public void Initialize(string ResourceName, bool IdQuery, bool Reset, string OptionString)
        {
            if (!_initialized)
            {
                _rangeCheck = true;
                _cache = true;
                _queryInstrumentStatus = false;
                _simulate = false;
                _driverSetup = "";
                _recordCoercions = false;
                _interchangeCheck = false;
                _accuracy = 0.0001;

                InitVisaMessageBasedSession(ResourceName);
                instrument.IO.Timeout = 10000;
                instrument.IO.SendEndEnabled = true;
                instrument.IO.TerminationCharacter = 13;                
                //instrument.IO.TerminationCharacterEnabled = true;
                //instrument.IO.Clear();
                ParseOptionString(OptionString);
                if (Reset)
                {
                    this.Reset();
                }                
                _initialized = true;
                this.SetControl(ControlStates.RemoteAndUnlocked);
                this.SetCommunicationProtocol(CommunicationProtocols.SendsLFAfterEachCR);
                currentState = getSolenoidState();
                this.Pause();
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_ALREADY_INITIALIZED));
            }
        }

        protected override void ConfigureInitialSettings(IIviStructure confInitialSettings)
        {
            foreach (IIviDataComponent dataComp in confInitialSettings.DataComponents)
            {

                string name = dataComp.Name;
                switch (name.ToLower())
                {
                    case "accuracy":
                        _accuracy = ((IIviReal)dataComp).Value;
                        break;
                    case "coilconstant":
                        try
                        {
                            double value = ((IIviReal)dataComp).Value;
                            coilConst = value;
                        }
                        catch (ArgumentException e)
                        {
                        }
                        break;
                    case "currentlimit":
                        try
                        {
                            double value = ((IIviReal)dataComp).Value;
                            //double limit;
                            if (value <= maxCurrentLimit)
                                currentLimit = value;
                            else
                                currentLimit = maxCurrentLimit;
                        }
                        catch (ArgumentException e)
                        {
                        }
                        break;

                    default:
                        iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_BAD_OPTION_NAME));
                        break;
                }
            }
        }

        #endregion

        #region IIviDriverUtility Members

        public void Disable()
        {
            if (_initialized)
            {
                GoToZero();                
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
            }
        }

        public override void Reset()
        {
            if (_initialized)
            {                
                GoToZero();
                interchangeWarningQueue.Enqueue("User called the Reset function");                
            }
            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));

            }
        }

        public override void ResetWithDefaults()
        {
            if (_initialized)
            {
                Reset();
            }

            else
            {
                iviDriverErrorQueue.Enqueue(new Error(IviDriver_ErrorCodes.E_IVI_NOT_INITIALIZED));
            }
        }

        #endregion


        #region IPositioningDevice Members

        PositioningDeviceState IPositioningDevice.State
        {
            get
            {
                return ConvertState(getSolenoidState());
            }
        }

        double IPositioningDevice.Position
        {
            get { return MagnetField; }
        }

        void IPositioningDevice.BeginMove(double position)
        {
            BeginGotoField(position);
        }

        void IPositioningDevice.Move(double position)
        {
            
                ProgrammedField = position;
                fieldSettled = false;
                if (currentState != SolenoidState.Running)
                {
                    currentState = SolenoidState.Running;
                    OnStateChangedEvent(new Solenoid.StateChangedEventArgs(SolenoidState.Running));
                    OnStateChangedEvent(new PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Running));
                }
                string cmd = "A1";
                string res = QueryAndWaitForSettling(cmd);
                if (res == "1")
                {
                    CheckAndReportState();
                    return;
                }
                SolenoidState state = SolenoidState.Error;
                OnStateChangedEvent(new Solenoid.StateChangedEventArgs(state));
                OnStateChangedEvent(new AIRO.Devices.PositioningDevice.StateChangedEventArgs(PositioningDeviceState.Error));
                currentState = state;
            
            
                return;
            
        }

        private EventHandler<PositioningDevice.StateChangedEventArgs> stateChangedEvent;
        private EventHandler<PositioningDevice.FinishEventArgs> finishEvent;
        private EventHandler<PositioningDevice.ProgressEventArgs> progressEvent;


        event EventHandler<PositioningDevice.StateChangedEventArgs> IPositioningDevice.StateChangedEvent
        {
            add { stateChangedEvent += value; }
            remove { stateChangedEvent -= value; }
        }

        event EventHandler<PositioningDevice.FinishEventArgs> IPositioningDevice.FinishEvent
        {
            add { finishEvent += value; }
            remove { finishEvent -= value; }
        }

        event EventHandler<PositioningDevice.ProgressEventArgs> IPositioningDevice.ProgressEvent
        {
            add { progressEvent += value; }
            remove { progressEvent -= value; }
        }

        protected virtual void OnProgressEvent(PositioningDevice.ProgressEventArgs e)
        {
            EventHandler<PositioningDevice.ProgressEventArgs> handler = progressEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnFinishEvent(PositioningDevice.FinishEventArgs e)
        {
            EventHandler<PositioningDevice.FinishEventArgs> handler = finishEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnStateChangedEvent(PositioningDevice.StateChangedEventArgs e)
        {
            EventHandler<PositioningDevice.StateChangedEventArgs> handler = stateChangedEvent;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        #endregion

        protected override string Query(string cmd)
        {            
            Debug.WriteLine("Program is about to send " + cmd);
            string strToWrite = cmd + "\r\n";
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] buffer = encoding.GetBytes(strToWrite);          
            
            byte[] rBuffer = null;
            int i=0;
            do
            {
                i++;
                try
                {
                    instrument.IO.Write(ref buffer, buffer.Length);
                    rBuffer = instrument.IO.Read(256);
                }
                catch { }
            } while (rBuffer == null);
            if (i != 1)
            {
                Debug.WriteLine("Ahtung! "+i.ToString());
            }
            string res=encoding.GetString(rBuffer);
            char[] charsToTrim = { '\n', '\r' };
            res= res.Trim(charsToTrim);
            Debug.WriteLine(cmd+" sent and "+ res+" received");
            return res;
        }

        protected override void SendCommand(string cmd)
        {
            Debug.WriteLine("Program is about to send " + cmd);
            string strToWrite = cmd + "\r\n";
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] buffer = encoding.GetBytes(strToWrite);
            instrument.IO.Write(ref buffer, buffer.Length);
            Debug.WriteLine(cmd + " sent");
        }
    }
}
